package com.xcity.game.mail;

import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.alibaba.fastjson.JSON;
import com.xcity.db.entity.Mail;
import com.xcity.db.entity.Player;
import com.xcity.db.mapper.MailMapper;
import com.xcity.game.common.Cause;
import com.xcity.game.common.MessageConst;
import com.xcity.game.player.PlayerService;
import com.xcity.pomelo.PomeloRoute;
import com.xcity.pomelo.push.msg.DotTipPush;
import com.xcity.pomelo.push.msg.NewMailPush;
import com.xcity.pomelo.request.BatchDeleteMailRequest;
import com.xcity.pomelo.request.BatchGetMailAttachsRequest;
import com.xcity.pomelo.request.DeleteMailRequest;
import com.xcity.pomelo.request.GetMailListRequest;
import com.xcity.pomelo.request.ReadMailRequest;
import com.xcity.pomelo.response.DeleteMailResponse;
import com.xcity.pomelo.response.HintResponse;
import com.xcity.pomelo.response.MailListResponse;
import com.xcity.pomelo.response.PlayerMoneyResponse;
import com.xcity.pomelo.response.ReadMailResponse;
import com.xcity.pomelo.response.Response;
import com.xcity.util.DBUtils;

import naga.x.App;
import naga.x.common.Updatable;
import naga.x.event.Event;
import naga.x.event.EventListener;
import naga.x.game.time.TimeUpdater;
import naga.x.net.Session;
import naga.x.net.annotation.OPHandler;
import naga.x.net.impl.pomelo.annotation.PomeloOP;
import naga.x.service.Service;
import naga.x.service.ServiceException;

@OPHandler
public class MailService implements Service, Updatable, EventListener {

	protected Queue<Mail> pendingNewMails = new LinkedBlockingQueue<Mail>();
	private long lastUpdateTime, lastDeleteTime;
	
	private static final int DEFAULT_UPDATE_INTERVAL = 2000; // 每两秒钟存一次邮件
	private static final int DEFAULT_DELETE_INTERVAL = 2 * 60 * 60 * 1000; // 每两小时删一次过期邮件
	private static final int FLUSH_SIZE = 600; // 每个cycle最多存600封邮件
	private static final int MAIL_COUNT_PER_PAGE = 100;
	
	protected static final Logger LOG = LoggerFactory.getLogger(MailService.class);
	
	@Override
	public void startup() throws ServiceException {
		App.getApp().getUpdater().addAsync(this);
//		App.getApp().getEventManager().register(this); // 升级不需要邮件提示 2017-02-22
	}

	@Override
	public void shutdown() throws ServiceException {
		flush(true);
	}

	@Override
	public String getId() {
		return getClass().getName();
	}

	@Override
	public boolean update() {
		flush(false);
		delete(); // 删除过期邮件
		return true;
	}
	
	void flush(boolean force) {
		if (TimeUpdater.getInstance().now() - lastUpdateTime > DEFAULT_UPDATE_INTERVAL || force) {
			lastUpdateTime = TimeUpdater.getInstance().now();
			Queue<Mail> mails = pendingNewMails;
			int count = 0;
			while (!mails.isEmpty()) {
				Mail mail = mails.poll();
				try {
					DBUtils.save(mail, MailMapper.class);
					Player dest = App.getApp().getServiceManager().get(PlayerService.class).getOnlinePlayer(mail.getDestId());
					if (dest != null) {
						dest.send(new NewMailPush(mail));
						dest.send(DotTipPush.NEW_MAIL);
					}
					LOG.info("[MAIL SAVE]ID[{}]MID[{}]MTITLE[{}]", mail.getDestId(), mail.getId(), mail.getTitle());
				} catch (Exception e) {
					LOG.error("[MAIL SAVE ERROR]" + JSON.toJSONString(mail), e);
				}
				if (!force && ++count == FLUSH_SIZE) {
					break;
				}
			}
		}
	}
	
	void delete() {
		if (TimeUpdater.getInstance().now() - lastDeleteTime > DEFAULT_DELETE_INTERVAL) {
			lastDeleteTime = TimeUpdater.getInstance().now();
			int c = DBUtils.delete(MailMapper.STATEMENT_DELETE_EXPIRE);
			LOG.info("[MAIL DELETE EXPIRE]COUNT[" + c + "]");
		}
	}
	
	public void sendSystemMail(long destId, String title, String content) {
		sendMail(new Mail(Mail.TYPE_SYSTEM, -1, destId, title, content));
	}
	
	public void sendSystemMail(long destId, String title, String content, MailAttach attach) {
		sendMail(new Mail(Mail.TYPE_SYSTEM, -1, destId, title, content, attach));
	}
	
	public void sendMail(byte type, long srcId, long destId, String title, String content) {
		sendMail(new Mail(type, srcId, destId, title, content));
	}
	
	public void sendMail(Mail mail) {
		pendingNewMails.offer(mail);
	}
	
	@PomeloOP(PomeloRoute.LIST_MAIL)
	protected void getMailList(final Session session, final GetMailListRequest req) {
		Player player = session.getClient();
		Map<String, Object> params = DBUtils.wrapParams("playerId", player.getId(), "offset", 0, "count", MAIL_COUNT_PER_PAGE);
		List<Mail> mails = DBUtils.selectList(MailMapper.STATEMENT_FIND_BY_PLAYER, params);
		session.send(new MailListResponse(req.getSerial(), mails));
	}
	
	@PomeloOP(PomeloRoute.READ_MAIL)
	protected void readMail(Session session, ReadMailRequest req) {
		Player player = session.getClient();
		Mail mail = DBUtils.find(req.getMailId(), MailMapper.class);
		if (mail == null || player.getId() != mail.getDestId()) {
			session.send(new HintResponse(req.getSerial(), MessageConst.MAIL_NOT_EXIST));
			return;
		}
		byte newState;
		if (req.getReceived() == 1) {
			newState = Mail.STATE_RECEIVED;
		} else {
			newState = Mail.STATE_READ;
		}
		if (mail.getState() != newState) {
			if (newState == Mail.STATE_RECEIVED) {
				receiveAttach(player, mail);
			} else {
				mail.setState(newState);
			}
			DBUtils.update(mail, MailMapper.class);
		}
		session.send(new ReadMailResponse(req.getSerial(), mail.getId(), mail.getState()));
	}
	
	@PomeloOP(PomeloRoute.DELETE_MAIL)
	protected void deleteMail(Session session, DeleteMailRequest req) {
		Player player = session.getClient();
		Mail mail = DBUtils.find(req.getMailId(), MailMapper.class);
		if (mail == null || player.getId() != mail.getDestId()) {
			session.send(new HintResponse(req.getSerial(), MessageConst.MAIL_NOT_EXIST));
			return;
		}
		if (mail.getAttachment() != null && mail.getState() != Mail.STATE_RECEIVED) {
			session.send(new HintResponse(req.getSerial(), MessageConst.MAIL_ATTACH_NOT_RECEIVE));
			return;
		}
		if (mail.getType() == Mail.TYPE_PVP_MSG) { // pvp消息特殊处理
			mail.setState(Mail.STATE_HIDE);
			DBUtils.update(mail, MailMapper.class);
		} else {
			DBUtils.delete(mail.getId(), MailMapper.class);
		}
		session.send(new DeleteMailResponse(req.getSerial(), mail.getId()));
		LOG.info("[MAIL DEL]ID[{}]MID[{}]MTITLE[{}]", player.getId(), mail.getId(), mail.getTitle());
	}
	
	@PomeloOP(PomeloRoute.BATCH_DEL_MAIL)
	protected void batchDeleteMail(Session session, BatchDeleteMailRequest req) {
		if (req.getType() != Mail.TYPE_NORMAL
				&& req.getType() != Mail.TYPE_SYSTEM
				&& req.getType() != Mail.TYPE_FINANICAL
				&& req.getType() != Mail.TYPE_PVP_MSG) {
			session.send(new HintResponse(req.getSerial(), MessageConst.ILLEGAL_OPERATION));
			return;
		}
		Player player = session.getClient();
		Map<String, Object> params = DBUtils.wrapParams("destId", player.getId(), "type", req.getType());
		DBUtils.delete(MailMapper.STATEMENT_DELETE_BY_TYPE, params);
		params.put("type", Mail.TYPE_PVP_MSG); // 删除pvp消息(隐藏)
		DBUtils.delete(MailMapper.STATEMENT_DELETE_BY_TYPE, params);
		session.send(new Response(req.getSerial()));
	}
	
	@PomeloOP(PomeloRoute.BATCH_RECEIVE_MAIL_ATTACHS)
	protected void batchReceiveMailAttachs(Session session, BatchGetMailAttachsRequest req) {
		Player player = session.getClient();
		Map<String, Object> params = DBUtils.wrapParams("playerId", player.getId(), "offset", 0, "count", MAIL_COUNT_PER_PAGE);
		List<Mail> mails = DBUtils.selectList(MailMapper.STATEMENT_FIND_BY_PLAYER, params);
		int size;
		if (mails != null && (size = mails.size()) > 0) {
			for (int i = 0; i < size; i++) {
				Mail mail = mails.get(i);
				if (mail.getState() != Mail.STATE_RECEIVED) {
					receiveAttach(player, mail);
					DBUtils.update(mail, MailMapper.class);
				}
			}
		}
		session.send(new PlayerMoneyResponse(req.getSerial(), session.getClient()));
	}
	
	private void receiveAttach(Player player, Mail mail) {
		if (mail.getState() == Mail.STATE_RECEIVED) {
			return;
		}
		mail.setState(Mail.STATE_RECEIVED);
		MailAttach attach = mail.getAttachment();
//		mail.setAttachment(null);
		if (attach != null && !attach.isEmpty()) {
			attach.receive(player, Cause.MAIL_ATTACH, false);
			LOG.info("[MAIL ATTACH GOT]ID[{}]MID[{}]MTITLE[{}]", player.getId(), mail.getId(), mail.getTitle());
		}
	}

	@Override
	public int[] getEventTypes() {
		return new int[] { /*GameEvents.EVENT_PLAYER_LEVEL_UP*/ };
	}

	@Override
	public void handleEvent(Event event) {
//		Player player = event.getParameter();
//		sendSystemMail(player.getId(),
//				MessageConst.LEVEL_UP_MAIL_TITLE,
//				MessageFormat.format(MessageConst.LEVEL_UP_MAIL_CONTENT, player.getLevel()));
	}

}
